#ifndef GRAPH_H
#define GRAPH_H
#include <iostream>
#include <map>
#include <utility>
#include <set>
#include <fstream>
using namespace std;
template <class vertexData,class edgeData>
class graph{
public:
    graph(){}
    void addEdge(vertexData first, vertexData second, edgeData data) {
        pair< vertexData,edgeData > pair1 (second,data);
        edgeMap.insert ( pair<vertexData,pair< vertexData,edgeData > >(first,pair1));
        vertexSet.insert(first);
        vertexSet.insert(second);
    }

    void addVertex(vertexData& vertex){
        vertexSet.insert(vertex);
    }

    typedef typename set < vertexData >::iterator vertexListIterator;
    typedef typename multimap< vertexData,pair< vertexData,edgeData > >::const_iterator edgeListIterator;
    class edgeIterator;
    class vertexIterator{
    friend class edgeIterator;
    public:


        vertexIterator() {}
        vertexIterator(const graph<vertexData, edgeData>* g, const vertexListIterator& v){
            graphPointer = g;
            listIterator = v;
        }
        ~vertexIterator() {}
        //TODO
        vertexData operator *() {
            return *listIterator;
        }
        const vertexData& operator *() const{
            return *listIterator;
        }
        edgeIterator beginEdges();
        const edgeIterator beginEdges() const;
        edgeIterator endEdges();
        const edgeIterator endEdges() const;

        vertexIterator(const vertexIterator& other){
                graphPointer = other.graphPointer;
                listIterator = other.listIterator;
            }

       vertexIterator& operator =(vertexIterator& other){
            graphPointer == other.graphPointer;
            listIterator == other.listIterator;
            return *this;
        }

        vertexIterator& operator ++(){
            listIterator ++;
            return *this;
        }

        //int GetIndexOfCurrentVertex() const;

        //bool operator <(const vertexIterator& other) const;

        bool operator ==(const vertexIterator& other) const{
            return (graphPointer == other.graphPointer)&&(listIterator == other.listIterator);
        }
        bool operator !=(const vertexIterator& other) const {
            return !(*this == other);
        }

    private:
        const graph<vertexData, edgeData>* graphPointer;
        vertexListIterator listIterator;
    };


    class edgeIterator{
    public:
        edgeIterator() {};
        edgeIterator(const edgeIterator& other){
            graphPointer = other.graphPointer;
            listIterator = other.listIterator;
        }
//
        edgeIterator(const graph<vertexData, edgeData>* g ,const edgeListIterator e){
            graphPointer = g;
            listIterator = e;
        }


        ~edgeIterator() {};

        /*edgeData& operator*(){
            return listIterator->second.second;
        }*/

        const edgeData& operator*() const{
            return listIterator->second.second;
        }

        vertexData first(){
            return listIterator->first;
        }

        vertexData second(){
            return listIterator->second.first;
        }

        vertexIterator beginVertex(){
            vertexIterator vertex(graphPointer, graphPointer->vertexSet.find(listIterator->first));
            return vertex;
        }

        const vertexIterator beginVertex() const{
            vertexIterator vertex(graphPointer, graphPointer->vertexSet.find(listIterator->first));
            return vertex;
        }
        vertexIterator endVertex(){
            vertexIterator vertex(graphPointer, graphPointer->vertexSet.find(listIterator->second->first));
            return vertex;
        }

        const vertexIterator endVertex() const{
            vertexIterator vertex(graphPointer, graphPointer->vertexSet.find(listIterator->second->first));
            return vertex;
        }

        edgeIterator& operator =(const edgeIterator& other){
            graphPointer = other.graphPointer;
            listIterator = other.listIterator;
            return *this;
        }

        edgeIterator& operator ++(){
            listIterator++;
            return *this;
        }

        bool operator ==(const edgeIterator& other) const{
            return (graphPointer == other.graphPointer)&&(listIterator == other.listIterator);
        }
        bool operator !=(const edgeIterator& other) const {
            return !operator==(other);
        }

    private:

        const graph<vertexData, edgeData>* graphPointer;
        edgeListIterator listIterator;
    };


        vertexIterator vertexBegin(){

            vertexIterator vertex (this,vertexSet.begin());
            return vertex;
        }

        vertexIterator vertexEnd(){
            vertexIterator vertex (this,vertexSet.end());
            return vertex;
        }

        vertexIterator findVertex(const vertexData& vertex) {
            for (graph<vertexData,edgeData>::vertexIterator v = this->vertexBegin(); v != this->vertexEnd(); ++v){
               if (*v == vertex){
                    return v;
                }
           }
           return this->vertexEnd();
        }

        edgeIterator edgeBegin(){
            edgeIterator edge (this,edgeMap.begin());
            return edge;
        }

        edgeIterator edgeEnd(){
            edgeIterator edge (this,edgeMap.end());
            return edge;
        }


    ~graph(){}
private:
    multimap< vertexData,pair< vertexData,edgeData > > edgeMap;
    set <vertexData> vertexSet;
};

template<class vertexData,class edgeData>
typename graph<vertexData,edgeData>::edgeIterator
graph<vertexData,edgeData>::vertexIterator::beginEdges(){
    edgeIterator edge (graphPointer,graphPointer->edgeMap.lower_bound(*listIterator));
    return edge;
}


template<class vertexData,class edgeData>
typename graph<vertexData,edgeData>::edgeIterator graph<vertexData,edgeData>::vertexIterator::endEdges(){
    edgeIterator edge (graphPointer,graphPointer->edgeMap.upper_bound(*listIterator));
    return edge;
}


#endif // GRAPH_H
